/*
 * Fuzzy
 * https://github.com/myork/fuzzy
 *
 * Copyright (c) 2012 Matt York
 * Licensed under the MIT license.
 */

// forked v 0.1.3, manually applied PR https://github.com/mattyork/fuzzy/pull/30
// updated with es6 export

var fuzzy = {}

// Return all elements of `array` that have a fuzzy
// match against `pattern`.
fuzzy.simpleFilter = function (pattern, array) {
  return array.filter(function (str) {
    return fuzzy.test(pattern, str)
  })
}

// Does `pattern` fuzzy match `str`?
fuzzy.test = function (pattern, str) {
  return fuzzy.match(pattern, str) !== null
}

// If `pattern` matches `str`, wrap each matching character
// in `opts.pre` and `opts.post`. If no match, return null
fuzzy.match = function (pattern, str, opts) {
  opts = opts || {}
  var patternIdx = 0
  var result = []
  var len = str.length
  var totalScore = 0
  var currScore = 0
  // prefix
  var pre = opts.pre || ''
  // suffix
  var post = opts.post || ''
  // String to compare against. This might be a lowercase version of the
  // raw string
  var compareString = opts.caseSensitive && str || str.toLowerCase() // eslint-disable-line no-mixed-operators
  var ch
  var matchIndices = []

  pattern = opts.caseSensitive && pattern || pattern.toLowerCase() // eslint-disable-line no-mixed-operators

  // For each character in the string, either add it to the result
  // or wrap in template if it's the next string in the pattern
  for (var idx = 0; idx < len; idx++) {
    ch = str[idx]
    if (compareString[idx] === pattern[patternIdx]) {
      ch = pre + ch + post
      patternIdx += 1

      // consecutive characters should increase the score more than linearly
      currScore += 1 + currScore
      matchIndices.push(idx)
    } else {
      currScore = 0
    }
    totalScore += currScore
    result[result.length] = ch
  }

  // return rendered string if we have a match for every char
  if (patternIdx === pattern.length) {
    // if the string is an exact match with pattern, totalScore should be maxed
    totalScore = (compareString === pattern) ? Infinity : totalScore
    return { rendered: result.join(''), score: totalScore, indices: matchIndices }
  }

  return null
}

// The normal entry point. Filters `arr` for matches against `pattern`.
// It returns an array with matching values of the type:
//
//     [{
//         string:   '<b>lah' // The rendered string
//       , index:    2        // The index of the element in `arr`
//       , original: 'blah'   // The original element in `arr`
//       , indices: [0]
//     }]
//
// `opts` is an optional argument bag. Details:
//
//    opts = {
//        // string to put before a matching character
//        pre:     '<b>'
//
//        // string to put after matching character
//      , post:    '</b>'
//
//        // Optional function. Input is an entry in the given arr`,
//        // output should be the string to test `pattern` against.
//        // In this example, if `arr = [{crying: 'koala'}]` we would return
//        // 'koala'.
//      , extract: function(arg) { return arg.crying; }
//    }
fuzzy.filter = function (pattern, arr, opts) {
  if (!arr || arr.length === 0) {
    return []
  }
  if (typeof pattern !== 'string') {
    return arr
  }
  opts = opts || {}
  return arr
    .reduce(function (prev, element, idx, arr) {
      var str = element
      if (opts.extract) {
        str = opts.extract(element)
      }
      var rendered = fuzzy.match(pattern, str, opts)
      if (rendered != null) {
        prev[prev.length] = {
          string: rendered.rendered,
          score: rendered.score,
          index: idx,
          original: element,
          indices: rendered.indices
        }
      }
      return prev
    }, [])

    // Sort by score. Browsers are inconsistent wrt stable/unstable
    // sorting, so force stable by using the index in the case of tie.
    // See http://ofb.net/~sethml/is-sort-stable.html
    .sort(function (a, b) {
      var compare = b.score - a.score
      if (compare) return compare
      return a.index - b.index
    })
}

export default fuzzy
